package com.atguigu.juc.sync;

import org.openjdk.jol.info.ClassLayout;

import java.util.concurrent.TimeUnit;

/**
 * @author yanglin
 * @create 2024-01-31 17:04 星期三
 * description:
 */
public class SynchronizedUpDemo {

    /**
     * 无锁：      001
     * 偏向锁：    101
     *      单线程竞争
     *          当前程A第一次竞争到锁时，通过操作修改Mark Word 中的偏向线程ID、设置为偏向模式
     *          如果不存在其他线程竞争，那么持有偏向锁的线程将永远不需要同步
     * 轻量级锁：  000
     *      存在多线程竞争，但任意时刻最多只有一个线程竞争，即不存在锁竞争太过激烈的情况，也就是没有线程阻塞
     *      有线程参与锁的竞争，但是获取锁的冲突时间极短
     *      本质就是自旋锁的CAS
     * 重锁：     010
     *      存在大量线程参与锁的竞争，冲突性很高
     */

    /**
     * 重量级锁：
     *      Java中synchronized的重量级锁，是基于进入和退出Monitor对象实现的。在编译时会将同步代码块的起始位置插入 monitor enter指令
     *      在结束位置插入 monitor exit 指令
     *      当线程执行到monitor enter 指令时，会尝试获取对象所对应的monitor 所有权，如果获取到了，即获取到了锁，会在monitor的owner中
     *      存放当前线程的id，这样它将处于锁定状态，除非退出同步块，否则其他线程无法获取到这个monitor
     */
    
    
    public static void main(String[] args) {
        Object o = new Object();
        new Thread(() -> {
            synchronized (o){
                System.out.println(ClassLayout.parseInstance(o).toPrintable());
            }
        }).start();
        new Thread(() -> {
            synchronized (o){
                System.out.println(ClassLayout.parseInstance(o).toPrintable());
            }
        }).start();
        new Thread(() -> {
            synchronized (o){
                System.out.println(ClassLayout.parseInstance(o).toPrintable());
            }
        }).start();
        new Thread(() -> {
            synchronized (o){
                System.out.println(ClassLayout.parseInstance(o).toPrintable());
            }
        }).start();
    }


    /**
     * 轻量级锁
     *      轻量级锁是为了线程近乎交替执行同步块时提高性能
     *      主要目的：在没有很多线程竞争的前提下，通过CAS减少重量级锁使用操作系统互斥量产生的性能消耗
     *      升级时机：当关闭偏向锁功能或多线程竞争偏向锁会导致偏向锁升级为轻量级锁
     * 轻量级锁和偏向锁的区别：
     *      争夺轻量级锁失败时，自旋尝试抢占锁
     *      轻量级锁每次退出同步块都需要释放锁，而偏向锁是在竞争发生时才释放锁
     */
    public static void lightweightLock() {
        //-XX:-UseBiasedLocking   禁用偏向锁功能，使其直接升级为轻量级锁 
        Object o = new Object();
        new Thread(() -> {
            synchronized (o){
                System.out.println(ClassLayout.parseInstance(o).toPrintable());
            }
        },"t1").start();
    }

    //-------------------------------------------------------------
    
    /**
     * 偏向锁：
     *  Hotspot的作者经过研究发现：
     *      多线程的情况下，锁不仅不存在多线程竞争，还存在锁由同一个线程多次获得的情况，偏向锁就是在这种情况下出现，
     *      它的出现为了解决只有一个线程执行同步时提高效率
     *
     *      偏向锁会偏向第一个访问锁的线程，如果在接下来的运行过程中，该锁没有被其他的线程访问，
     *      则持有偏向锁的线程永远不需要触发同步。也即偏向锁在资源没有竞争情况下消除了同步语句，
     *      连CAS操作都不做，直接提高程序性能
     *
     * 偏向锁撤销：
     *      当由另外的线程逐步来竞争锁的时候，就不能在使用偏向锁了，要升级为轻量级锁
     *      竞争线程尝试CAS更新对象头失败，会等待到全局安全点（此时不会执行任何代码）撤销偏向锁
     *
     *  java -XX:+PrintFlagsInitial | grep BiasedLock*
     */
    public static void biasedLock2() {
        //偏向锁默认开启，但延迟4秒，这里通过让程序暂停的方式，来等待偏向锁启动成功（不需要设置虚拟机参数）
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Object o = new Object();
        System.out.println(ClassLayout.parseInstance(o).toPrintable());   //在没锁的情况下，偏向锁无线程id？？？？？

        System.out.println("===============");
        new Thread(() -> {
            synchronized (o){
                System.out.println(ClassLayout.parseInstance(o).toPrintable());
            }
        },"t1").start();
    }

    public static void biasedLock1() {
        //偏向锁默认开启，但延迟4秒，这里通过让程序暂停的方式，来等待偏向锁启动成功（不需要设置虚拟机参数）
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //biased
        Object o = new Object();
        //-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0    开启偏向锁，并且关闭其延迟使其立即启动（java6以后默认开启）
        synchronized (o){
            System.out.println(ClassLayout.parseInstance(o).toPrintable());
        }
    }

    public static void noLock() {
        Object o = new Object();
        System.out.println("Decimal：" + o.hashCode());    //十进制
        System.out.println("Hexadecimal：" + Integer.toHexString(o.hashCode()));    //十六进制
        System.out.println("Binary：" + Integer.toBinaryString(o.hashCode()));       //二进制
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
    }

}
